import { KEYBOARD_KEY } from 'nextcae-base';
import { EntityInfo, EntityPartType, THREE } from '@amcax/web-render';
import {
  formatPoints,
  getCurveLastDistPoint,
  getNormalByPoints,
  makeId,
  mouseOnPlane,
  nmAppDebugLog,
} from '../utils';
import { InitType, longPressThreshold } from '../types/command/types';
import _ from 'lodash';
import { EventsRes } from '@amcax/base';
import { RenderBase } from './renderBase';
enum ListenType {
  ADD = 'addEventListener',
  REMOVE = 'removeEventListener',
}
export default abstract class Base extends RenderBase {
  private isShift: boolean = false; // 是否shift
  public isHovered = false;
  private pressTimer = null;
  private mouseLongPress = false;
  public exitFunc: () => void;
  private handShift: () => void;
  abstract onMouseLeft(e: EntityInfo, selected?: boolean): void;
  get shift() {
    return this.isShift;
  }
  get getNormalByPoints() {
    return getNormalByPoints;
  }
  constructor({ enableBoxSelect = false, entityPartType }: InitType = {}) {
    super();
    this.enableBoxSelect = enableBoxSelect;
    entityPartType && (this.pickType = entityPartType);
    this.handShift = _.debounce(this.onShiftUp, 500);
  }
  public shiftUp = () => {}; // shift 弹起
  public shiftDown = () => {}; // shift 按下
  // shift 弹起事件
  private onShiftUp = () => {
    if (this.isShift) {
      this.isShift = false;
      this.shiftUp();
    }
  };
  // shift 按下事件
  private onKeyDown = (e) => {
    if (e.key === 'Shift') {
      this.isShift = true;
      this.handShift();
      this.shiftDown();
    }
  };
  private otherKey = (e: EventsRes<KeyboardEvent>) => {
    if (e.key === 'Shift') {
      this.onShiftUp();
    }
  };
  // 注册事件
  public registerEvents() {
    this.mouseEvent();
    this.keyEvent();
    this.registerPickEvents();
  }
  // 注册渲染监听事件
  public registerPickEvents() {
    this.interactionMgr
      .addBeforeHovered(this.onBeforeHovered)
      .addHoveredListener(this.onHoveredBase)
      .addClickListener(this.onMouseLeft)
      .addBoxSelectListener(this.onMultipleSelected);
  }
  // 鼠标事件
  public mouseEvent(isAdd: boolean = true) {
    const key = isAdd ? ListenType.ADD : ListenType.REMOVE;
    this.mainRenderer.container[key]('mousedown', this.onMouseDown);
    this.mainRenderer.container[key]('mouseup', this.onMouseUp);
    this.mainRenderer.container[key]('mousemove', this.onMoved);
  }
  // 键盘事件
  public keyEvent(isAdd: boolean = true) {
    const key = isAdd ? ListenType.ADD : ListenType.REMOVE;
    window[key]('keyup', this.onKeyUp);
    window[key]('keydown', this.onKeyDown);
  }
  // 注销事件
  public unregisterEvents() {
    this.mouseEvent(false);
    this.keyEvent(false);
    this.unregisterPickEvents();
    this.interactionMgr.pickType = EntityPartType.all;
    this.interactionMgr.enableBoxSelect = false;
  }
  // 注销渲染监听事件
  public unregisterPickEvents() {
    this.interactionMgr
      .removeBeforeHovered(this.onBeforeHovered)
      .removeHoveredListener(this.onHoveredBase)
      .removeClickListener(this.onMouseLeft)
      .removeBoxSelectListener(this.onMultipleSelected);
  }
  // hover之前事件 用于判断是否hover
  onBeforeHovered = (e: EntityInfo) => true;
  // 鼠标移动事件
  onMoved = (e) => {
    if (!this.isHovered) {
      this.onHoveredNull(e);
    }
    this.isHovered = false;
  };
  private onHoveredBase = (e) => {
    this.isHovered = true;
    this.onHovered(e);
  };
  // hover 到实体
  onHovered = (e) => e;
  // hover 到空白处
  onHoveredNull: (e) => void = (e) => e;
  // 键盘事件
  onKeyUp = (e) => {
    if (e.key === KEYBOARD_KEY.ESC) {
      this.exitFunc?.();
    } else if (e.key === KEYBOARD_KEY.ENTER) {
      this.onEnter();
    } else {
      this.otherKey(e);
    }
  };
  // 框选
  onMultipleSelected = (selectArr: EntityInfo[]): void => {
    nmAppDebugLog('onMultipleSelected', { selectArr });
  };
  onEnter: () => void = () => {};
  // 鼠标事件
  onMouseUp = (e) => {
    this.clearTimer();
    if (e.button === 1 && !this.mouseLongPress) {
      this.onMouseMid(e);
    } else if (e.button === 2) {
      this.onMouseRight(e);
    } else if (e.button === 0) {
      this.onMouseLeftPro(e);
    }
  };
  onMouseLeftPro: (e) => void = (e) => e;
  private onMouseDown = () => {
    this.mouseLongPress = false;
    // 启动长按计时器
    this.pressTimer = setTimeout(() => {
      this.mouseLongPress = true;
    }, longPressThreshold);
  };
  // 清理计时器
  private clearTimer = () => {
    if (this.pressTimer) {
      clearTimeout(this.pressTimer);
      this.pressTimer = null;
    }
  };
  onMouseMid: (e) => void = (e) => e;
  onMouseRight: (e) => void = (e) => e;
  /**
   * 根据entityInfo，生成后端需要的id
   * @param info
   * @returns 后端需要的id
   */
  static makeId(info?: EntityInfo) {
    return makeId(info);
  }
  /**
   *  // 计算鼠标在指定平面内与的相机连线的交点
   * @param x 鼠标x坐标
   * @param y 鼠标x坐标
   * @param planeNormal 指定平面的法向量
   * @param ptOnPlane 指定平面上的一点
   * @returns
   */
  public mouseOnPlane(
    x: number,
    y: number,
    planeNormal = new THREE.Vector3(0, 0, 1),
    ptOnPlane = new THREE.Vector3(0, 0, 0),
  ) {
    const mouse = new THREE.Vector2();
    mouse.x = (x / this.mainRenderer.container.clientWidth) * 2 - 1;
    mouse.y = -(y / this.mainRenderer.container.clientHeight) * 2 + 1;
    return mouseOnPlane(mouse, { planeNormal, ptOnPlane, camera: this.camera });
  }
  /**
   * 获取线的点
   * */
  public getLinePoints(hoverInfo: EntityInfo) {
    const points = this.getMgr().getEntityData(
      hoverInfo.dxid,
      EntityPartType.line,
      hoverInfo.idx,
    );
    return formatPoints(points).map((p) => new THREE.Vector3(...p));
  }
  /**
   * 获取当前实体的normal
   * */
  public getNormal(hoverInfo: EntityInfo, point: THREE.Vector3) {
    const normal = hoverInfo?.normal;
    if (hoverInfo.type === EntityPartType.line) {
      const edgePoints = this.getLinePoints(hoverInfo);
      const { p } = getCurveLastDistPoint(point, edgePoints); // 最近点和最近点的索引
      return new THREE.Vector3().subVectors(p[1], p[0]).normalize(); // 线段的方向
    }
    return normal ? new THREE.Vector3(...normal) : null;
  }
  public clear() {
    // 清除选中实体
    this.clearSelected();
    // 清除辅助group
    this.clearAssisMgr();
    // 进行选中、设置颜色等渲染相关的操作后，都需要主动调用一次render
    this.mainRenderer.render();
  }
  public destroy() {
    // 注销事件
    this.unregisterEvents();
    this.release(); // 释放资源
    this.clear(); // 清除选中实体和辅助group
  }
  // 调用组件，共用释放资源
  release() {
    throw new Error('Method not implemented.');
  }
}
